package com.cata.mall.common.config;

import com.cata.mall.common.domain.SwaggerProperties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @作者: Tao
 * @日期: 2025/10/10
 * @时间: 16:07
 * @描述: Swagger基础配置<pre>
 *
 *     主要作用：
 *  -@Bean
 *  public Docket createRestApi() {
 *     // 创建 Docket 实例
 *     Docket docket = new Docket(DocumentationType.SWAGGER_2)
 *             // 设置 API 文档基本信息
 *             .apiInfo(new ApiInfoBuilder()
 *                 .title("商城系统 API 文档")
 *                 .description("CTMall 商城系统后台接口文档")
 *                 .version("1.0")
 *                 .build())
 *             // 配置 API 扫描路径
 *             .select()
 *             .apis(RequestHandlerSelectors.basePackage("com.cata.mall.controller"))
 *             .paths(PathSelectors.any())
 *             .build();
 *     // 配置 JWT 认证
 *     docket.securitySchemes(Arrays.asList(
 *         new ApiKey("Authorization", "Authorization", "header")
 *     ));
 *     return docket;
 * }
 *
 * 这样配置后，Swagger 会自动扫描 com.cata.mall.controller 包下的所有接口，并生成交互式的 API 文档页面
 */
public abstract class BaseSwaggerConfig {

    /**
     * 创建Swagger API文档配置Bean
     *
     * @return Docket对象，用于配置Swagger API文档生成器
     */
    @Bean
    public Docket createRestApi() {
        // 获取Swagger配置属性
        SwaggerProperties swaggerProperties = swaggerProperties();

        // 创建Docket实例并配置基本信息、API选择器
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo(swaggerProperties))
                .select()
                .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getApiBasePackage()))
                .paths(PathSelectors.any())
                .build();

        // 如果启用安全配置，则添加安全方案和安全上下文
        if (swaggerProperties.isEnableSecurity()) {
            docket.securitySchemes(securitySchemes()).securityContexts(securityContexts());
        }

        return docket;
    }


    /**
     * 构建API信息对象
     *
     * @param swaggerProperties Swagger配置属性对象，包含API的基本信息
     * @return ApiInfo API信息对象，用于Swagger文档展示
     */
    private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
        // 使用Swagger属性构建API信息
        return new ApiInfoBuilder()
                .title(swaggerProperties.getTitle())
                .description(swaggerProperties.getDescription())
                .contact(new Contact(swaggerProperties.getContactName(), swaggerProperties.getContactUrl(), swaggerProperties.getContactEmail()))
                .version(swaggerProperties.getVersion())
                .build();
    }

    /**
     * 配置API文档的安全策略方案
     *
     * @return 包含安全策略方案的列表
     */
    private List<SecurityScheme> securitySchemes() {
        //设置请求头信息
        List<SecurityScheme> result = new ArrayList<>();
        ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
        result.add(apiKey);
        return result;
    }


    /**
     * 获取安全上下文列表
     *
     * @return 包含指定路径安全上下文的列表
     */
    private List<SecurityContext> securityContexts() {
        //设置需要登录认证的路径
        List<SecurityContext> result = new ArrayList<>();
        result.add(getContextByPath("/*/.*"));
        return result;
    }


    /**
     * 根据路径正则表达式创建安全上下文
     *
     * @param pathRegex 路径匹配的正则表达式
     * @return 构建好的安全上下文对象
     */
    private SecurityContext getContextByPath(String pathRegex) {
        // 构建安全上下文，设置安全引用和操作选择器
        return SecurityContext.builder()
                //返回安全认证引用列表，用于配置API访问所需的安全认证信息
                .securityReferences(defaultAuth())
                //用于匹配请求路径的正则表达式，只有匹配的路径才会被包含在API文档中
                .operationSelector(oc -> oc.requestMappingPattern().matches(pathRegex))
                .build();
    }


    /**
     * 获取默认的安全认证配置列表
     *
     * @return 包含安全引用配置的列表，用于Swagger API文档的全局认证设置
     */
    private List<SecurityReference> defaultAuth() {
        // 创建安全引用列表
        List<SecurityReference> result = new ArrayList<>();

        // 定义全局访问权限范围
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;

        // 添加基于Authorization头的安全引用配置
        result.add(new SecurityReference("Authorization", authorizationScopes));
        return result;
    }

    /**
     * 生成BeanPostProcessor实例，用于自定义Springfox的处理器映射
     * 该处理器在Bean初始化完成后进行后处理，主要针对WebMvc和WebFlux的请求处理提供者
     *
     * @return BeanPostProcessor实例
     */
    public BeanPostProcessor generateBeanPostProcessor(){
        return new BeanPostProcessor() {

            /**
             * 在Bean初始化完成后进行后处理
             * 如果Bean是WebMvcRequestHandlerProvider或WebFluxRequestHandlerProvider的实例，
             * 则对处理器映射进行自定义配置
             *
             * @param bean Bean实例
             * @param beanName Bean名称
             * @return 处理后的Bean实例
             * @throws BeansException Spring Bean异常
             */
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                    customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
                }
                return bean;
            }

            /**
             * 自定义Springfox的处理器映射列表
             * 过滤掉包含模式解析器的映射，只保留没有模式解析器的映射
             *
             * @param mappings 请求映射信息处理器映射列表
             * @param <T> RequestMappingInfoHandlerMapping的子类型
             */
            private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
                // 过滤出没有模式解析器的映射并创建副本
                List<T> copy = mappings.stream()
                        .filter(mapping -> mapping.getPatternParser() == null)
                        .collect(Collectors.toList());
                mappings.clear();
                mappings.addAll(copy);
            }

            /**
             * 通过反射获取Bean中的handlerMappings字段值
             *
             * @param bean 目标Bean对象
             * @return RequestMappingInfoHandlerMapping列表
             */
            @SuppressWarnings("unchecked")
            private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
                try {
                    Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
                    field.setAccessible(true);
                    return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    throw new IllegalStateException(e);
                }
            }
        };
    }


    /**
     * 自定义Swagger配置
     */
    public abstract SwaggerProperties swaggerProperties();
}
